// #include "src/include/LPA.h"
#include "src/include/TPA.h"
#include "src/include/HPA.h"
#include "src/include/LPA.h"
#include <cstdlib>
#include <ctime>
#include <random>

int rand_int(int min, int max) {
    static std::mt19937 gen(std::time(0));
    std::uniform_int_distribution<> distr(min, max);
    return distr(gen);
}

int main() {
    Logger::init(LOG_PATH);
    const int NUM = 32;
    flow_num = NUM;

    for (int i = 1; i <= flow_num/2; ++i) {
        auto &f = flows[i];
        f.id = i;
        f.src = 0;
        f.dst = 1;
        f.critical = 0;
    }
    for (int i = flow_num/2 + 1; i <= flow_num; ++i) {
        auto &f = flows[i];
        f.id = i;
        f.src = 0;
        f.dst = 1;
        f.critical = 1;
    }
    int period[] = {300, 500, 1000, 1200, 1500, 1800, 2000, 2400, 2500, 3000, 3500, 4000, 5000};
    const int totPeriod = sizeof(period) / sizeof(period[0]);
    int tot = 0, good = 0, bad = 0;

    while (true) {
        ++ tot;
        for (int i = 1; i <= flow_num/2; ++i) {
            auto &f = flows[i];
            f.period = period[rand_int(0, totPeriod - 1)];
            // f.period = rand_int(500, 4000);
            f.relative_dl = rand_int(1000, 5000);
        }
        std::stable_sort(flows + 1, flows + flow_num/2 + 1, [](const flow& a, const flow& b) {
            if (a.relative_dl == b.relative_dl)
                return a.period > b.period;
            return a.relative_dl > b.relative_dl;
        });
        for (int i = flow_num/2+1; i <= flow_num; ++i) {
            auto& f = flows[i];
            f.period = period[rand_int(0, totPeriod - 1)];
            f.relative_dl = rand_int(500, 3000);
        }
        std::stable_sort(flows + flow_num/2 + 1, flows + flow_num, [](const flow& a, const flow& b) {
            if (a.relative_dl == b.relative_dl)
                return a.period > b.period;
            return a.relative_dl > b.relative_dl;
        });

        for (auto& f : flows) {
            f.size = rand_int(100, 300);
        }
        for (auto& f : flows) {
            f.cw = CBIT_WRITE * f.size * 8;
            f.cr = CBIT_READ * f.size * 8;
        }
        get_HP();
        HPA *prio_assign = new HPA(flows);
        prio_assign->run();
        prio_assign->debug_flows();
        prio_assign->copy_flows(flows);
        int cnt[NUM + 1] = {0};
        int old_sche_cnt[NUM + 1] = {0}, new_sche_cnt[NUM + 1] = {0};
        int old_sche_high[NUM + 1] = {0}, new_sche_high[NUM + 1] = {0};
        int old_sche_low_tot = 0, old_sche_high_tot = 0;
        int new_sche_low_tot = 0, new_sche_high_tot = 0;
        int mi_prio = NUM;
        for (int i = 1; i <= NUM; i++) {
            if (flows[i].is_scheduled) {
                old_sche_cnt[flows[i].old_priority]++;
                if (flows[i].critical) old_sche_high[flows[i].old_priority]++, old_sche_high_tot++;
                else old_sche_low_tot++;
            }
            mi_prio = std::min(mi_prio, flows[i].old_priority);
        }
        if (old_sche_high_tot > NUM / 2 - 4 || mi_prio > 25) {
            delete prio_assign;
            continue;
        }
	    printf("\nafter separate\n");
        prio_assign->separate_ep_flows();
        prio_assign->debug_flows();
        prio_assign->copy_flows(flows);
        // prio_assign->record_flows(HPA_RESULT_CSV_PATH);

        for (int i = 1; i <= NUM; i++) {
            if (flows[i].is_scheduled) {
                new_sche_cnt[flows[i].old_priority]++;
                if (flows[i].critical) new_sche_high[flows[i].old_priority]++, new_sche_high_tot++;
                else new_sche_low_tot++;
            }
        }
        if (new_sche_high_tot > NUM / 2 - 4 || new_sche_low_tot > NUM / 2 - 4) {
            delete prio_assign;
            continue;
        }
        // if (new_sche_high_tot > old_sche_high_tot ||
        //     (new_sche_high_tot == old_sche_high_tot && new_sche_low_tot > old_sche_low_tot)) {
        //     ++ good;
        // } else if (new_sche_high_tot < old_sche_high_tot ||
        //     (new_sche_high_tot == old_sche_high_tot && new_sche_low_tot < old_sche_low_tot)) {
        //     ++ bad;
        //     prio_assign->debug_flows();
        //     prio_assign->record_flows(HPA_RESULT_CSV_PATH);
        //     break;
        // }
        int mi = -1;
        int tar = 0;
        int flag = 0;
        for (int i = 1;i < NUM;i++) {
            if (new_sche_cnt[i] && mi != -1 && new_sche_cnt[i] > old_sche_cnt[i] + 2) {
                flag = 1;
                tar = i;
                break;
            }
            if (new_sche_cnt[i] && mi == -1) mi = i;
        }
        // if (tot % 1000 == 0) {
        //     printf("tot: %d, good: %d, bad: %d, rate: %f\n", tot, good, bad, (double)good / tot);
        // }
        if (flag){
            printf("mi: %d, tar: %d\n", mi, tar);
            prio_assign->record_flows(HPA_RESULT_CSV_PATH);
            break;
        }
        delete prio_assign;
        // printf("\n**** try %d ****\n", ++tot);
    }


    return 0;
}
